Fork me on GitHub

HandlerExceptionResolver

注意:所有文章除特别说明外,转载请注明出处.

第16章 HandlerExceptionResolver

HandlerExceptionResolver主要实现都继承与抽象类的AbstractHandlerExecutionResolver,它有五个子类,其中AnnotationMethodHandlerExceptionResolver已经被弃用。

1.AbstractHandlerMethodExceptionResolver和其子类ExceptionHandlerExcpetionResolver一起完成 @ExceptionHandler 注释的方法进行异常解析的功能。

2.DefaultHandlerExceptionResolver按照不同类型分别对异常进行解析。

3.ResponseStatusExceptionResolver解析@ResponseStatus注释类型的异常。

4.SimpleMappingExceptionResolver:通过配置的异常类和view的对应关系解析异常。

5.HandlerExecutionResolverComposite作为一个容器使用,里面封装了别的Resolver。

提示:异常解析的过程是:1.给ModelAndView设置相应的内容,设置response的相关属性。

16.1 AbstractHandlerExceptionResolver

该类是所有直接解析异常类的父类,在里面定义了通用的解析流程,并使用模板模式,子类只需要覆盖相应的方法即可。

@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    //判断当前ExceptionResolver是否可以解析所传入处理器抛出的异常,这里可以指定只能处理指定的处理器抛出的异常。如果不可以返回null,交给下一个解析
    if (this.shouldApplyTo(request, handler)) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
        }
        //设置response
        this.prepareResponse(ex, response);
        //调用这个实际解析异常,这是模板方法交给子类实现
        ModelAndView result = this.doResolveException(request, response, handler, ex);
        if (result != null) {
            this.logException(ex, request);
        }

        return result;
    } else {
        return null;
    }
}

protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler{
    //这里的mappedHandlers用于配置处理器的集合,mappedHandlerClasses用于配置处理器类型的集合,如果设置了这两个属性的一个,那么ExceptionResolver就只能解析所设置的处理器抛出的异常    
    if (handler != null) {
        if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
            return true;
        }

        if (this.mappedHandlerClasses != null) {
            Class[] var3 = this.mappedHandlerClasses;
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Class<?> handlerClass = var3[var5];
                if (handlerClass.isInstance(handler)) {
                    return true;
                }
            }
        }
    }

    return this.mappedHandlers == null && this.mappedHandlerClasses == null;
}

16.2 ExceptionHandlerExceptionResolver

这个类继承AbstractHandlerMethodExceptionResolver,后者又继承于AbstractHandlerMethodExceptionResolver重写了shouldApplyTo方法,并且在处理请求的doResolverException方法中将实际处理请求的过程交给了模板方法doResolveHanlerMethodException。

提示:AbstractHandlerMethodExceptionResolver作用相当于适配器。一般的处理器是类的形式,但HandlerMethod是将方法作为处理器使用,所以需要适配。1.首先在shouldApplyTo()中判断如果处理器是HandlerMethod类型则将处理器设置为其所在类,然后再交给父类判断。如果为空则直接交给父类判断。如果既不为空也不是HandlerMethod类型则返回false不处理。

ExceptionHandlerExceptionResolver其实是一个简化版的RequestMappingHandlerAdapter,它的执行也是使用ServletInvocableHandlerMethod。1.首先根据handlerMethod和exception将其创建出来(找出所有注释了@ExceptionHandler的方法,然后根据其配置中的异常和需要解析的异常进行匹配)。2.然后设置argumentResolver和returnValueHandlers。3.接着调用其invokeAndHandle()方法执行处理。4.最后将处理结果封装成ModelAndView返回。

@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
    //找到处理异常的方法
    ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
        return null;
    } else {
         //设置argumentResolvers和returnValueHandlers
        if (this.argumentResolvers != null) {
            exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }

        if (this.returnValueHandlers != null) {
            exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();

        try {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
            }

            Throwable cause = exception.getCause();
            if (cause != null) {
                //执行解析异常
                exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, cause, handlerMethod});
            } else {
                exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, handlerMethod});
            }
        } catch (Throwable var12) {
            if (var12 != exception && this.logger.isWarnEnabled()) {
                this.logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, var12);
            }

            return null;
        }

        if (mavContainer.isRequestHandled()) {
            return new ModelAndView();
        } else {
            //封装ModelAndView 
            ModelMap model = mavContainer.getModel();
            HttpStatus status = mavContainer.getStatus();
            ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
            mav.setViewName(mavContainer.getViewName());
            if (!mavContainer.isViewReference()) {
                mav.setView((View)mavContainer.getView());
            }

            if (model instanceof RedirectAttributes) {
                Map<String, ?> flashAttributes = ((RedirectAttributes)model).getFlashAttributes();
                RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
            }

            return mav;
        }
    }
}

注意:这里只是返回了ModelAndView,并没有对response进行设置。如果需要可以自行在异常处理器中设置。

## 16.3 DefaultHandlerExceptionResolver

该解析器的解析过程是根据异常类型的不同,使用不同的方法进行处理。

@Nullable
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    try {
        if (ex instanceof HttpRequestMethodNotSupportedException) {
            return this.handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException)ex, request, response, handler);
        }

        if (ex instanceof HttpMediaTypeNotSupportedException) {
            return this.handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException)ex, request, response, handler);
        }

        if (ex instanceof HttpMediaTypeNotAcceptableException) {
            return this.handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException)ex, request, response, handler);
        }

        if (ex instanceof MissingPathVariableException) {
            return this.handleMissingPathVariable((MissingPathVariableException)ex, request, response, handler);
        }

        if (ex instanceof MissingServletRequestParameterException) {
            return this.handleMissingServletRequestParameter((MissingServletRequestParameterException)ex, request, response, handler);
        }

        if (ex instanceof ServletRequestBindingException) {
            return this.handleServletRequestBindingException((ServletRequestBindingException)ex, request, response, handler);
        }

        if (ex instanceof ConversionNotSupportedException) {
            return this.handleConversionNotSupported((ConversionNotSupportedException)ex, request, response, handler);
        }

        if (ex instanceof TypeMismatchException) {
            return this.handleTypeMismatch((TypeMismatchException)ex, request, response, handler);
        }

        if (ex instanceof HttpMessageNotReadableException) {
            return this.handleHttpMessageNotReadable((HttpMessageNotReadableException)ex, request, response, handler);
        }

        if (ex instanceof HttpMessageNotWritableException) {
            return this.handleHttpMessageNotWritable((HttpMessageNotWritableException)ex, request, response, handler);
        }

        if (ex instanceof MethodArgumentNotValidException) {
            return this.handleMethodArgumentNotValidException((MethodArgumentNotValidException)ex, request, response, handler);
        }

        if (ex instanceof MissingServletRequestPartException) {
            return this.handleMissingServletRequestPartException((MissingServletRequestPartException)ex, request, response, handler);
        }

        if (ex instanceof BindException) {
            return this.handleBindException((BindException)ex, request, response, handler);
        }

        if (ex instanceof NoHandlerFoundException) {
            return this.handleNoHandlerFoundException((NoHandlerFoundException)ex, request, response, handler);
        }

        if (ex instanceof AsyncRequestTimeoutException) {
            return this.handleAsyncRequestTimeoutException((AsyncRequestTimeoutException)ex, request, response, handler);
        }
    } catch (Exception var6) {
        if (this.logger.isWarnEnabled()) {
            this.logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in exception", var6);
        }
    }

    return null;
}

提示:具体的解析方法很简单,主要是设置response的相关属性。

没有找到处理器执行方法和request的Method类型不支持异常处理

16.4 ResponseStatusExceptionResolver

该解析器用来解析注视了@ResponseStatus的异常。

protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    try {
        if (ex instanceof ResponseStatusException) {
            return this.resolveResponseStatusException((ResponseStatusException)ex, request, response, handler);
        }
        //找到@ResponseStatus注释
        ResponseStatus status = (ResponseStatus)AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
        if (status != null) {
            //然后调用解析方法
            return this.resolveResponseStatus(status, request, response, handler, ex);
        }

        if (ex.getCause() instanceof Exception) {
            ex = (Exception)ex.getCause();
            return this.doResolveException(request, response, handler, ex);
        }
    } catch (Exception var6) {
        this.logger.warn("ResponseStatus handling resulted in exception", var6);
    }

    return null;
}

protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
    int statusCode = responseStatus.code().value();
    String reason = responseStatus.reason();
    return this.applyStatusAndReason(statusCode, reason, response);
}

16.5 SimpleMappingExceptionResolver

该解析器需要提前配置异常类和view的对应关系后才能使用。

protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    //首先根据异常查找显示错误的逻辑视图
    String viewName = this.determineViewName(ex, request);

    if (viewName != null) {

        //检查是否配置了所找到的viewName对应的statusCode
        Integer statusCode = this.determineStatusCode(request, viewName);

        if (statusCode != null) {

            //设置response
            this.applyStatusCodeIfPossible(request, response, statusCode);
        }
        //将异常和解析出的viewName封装成ModelAndView并返回
        return this.getModelAndView(viewName, ex, request);
    } else {
        return null;
    }
}

@Nullable
protected String determineViewName(Exception ex, HttpServletRequest request) {
    String viewName = null;

    //如果异常在设置的excludeExceptions中包含则返回null
    if (this.excludedExceptions != null) {
        Class[] var4 = this.excludedExceptions;
        int var5 = var4.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            Class<?> excludedEx = var4[var6];
            if (excludedEx.equals(ex.getClass())) {
                return null;
            }
        }
    }

    //调用findMatchingViewName方法实际查找
    if (this.exceptionMappings != null) {
        viewName = this.findMatchingViewName(this.exceptionMappings, ex);
    }

    //如果没有找到viewName且配置了defaultErrorView,则采用默认视图
    if (viewName == null && this.defaultErrorView != null) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" + ex.getClass().getName() + "]");
        }

        viewName = this.defaultErrorView;
    }

    return viewName;
}

提示:这里首先检查异常是不是配置在excludedException中(excludedException用于配置不处理的异常),如果是则返回null,否则调用findMatchingViewName实际查找viewName,如果没有找到且配置了defaultErrorView,则使用defaultErrorView。findMatchingViewName()方法从传入的参数就可以看出它是根据配置的exceptionMapping参数匹配当前异常的,不过并不是直接完全匹配,而是只要配置异常的字符在当前处理的异常或其父类中存在就可以了。

protected String findMatchingViewName(Properties exceptionMapping, Exception ex) {
    String viewName = null;
    String dominantMapping = null;
    int deepest = Integer.MAX_VALUE;
    ...
} 

总结:mvc:annotation-driven/ 会自动将ExceptionHandlerExceptionResolver | DefaultHandlerExceptionResolver | ResponseStatusExceptionResolver配置到SpringMVC中,如果SimpleMappingExceptionResolver使用时需要配置,同时该异常只能处理请求处理过程抛出的异常,异常处理本身抛出的异常和视图解析过程中抛出的异常时不能做到的。

本文标题:HandlerExceptionResolver

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:17:09

原始链接:http://bangjinhu.github.io/undefined/第16章 HandlerExceptionResolver/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.